package dovs.instructions;

import java.util.HashMap;
import java.util.Map;

/**
 * Base class for all instruction classes. Some instruction classes correspond
 * to one of a number of different actual JVM instructions, depending on the
 * values of its parameters.
 */
public abstract class Instruction {
	/**
	 * An identification number associated with the instruction type.
	 * 
	 * @see #getID
	 */
	private int id;

	private static Map<Class<? extends Instruction>, Integer> inst_id = new HashMap<Class<? extends Instruction>, Integer>();

	private static int next_id = 0;

	public Instruction() {
		id = getID(getClass());
	}

	/**
	 * Returns an identification number associated with the instruction type.
	 * Each instruction class gets a unique ID which can be used to do efficient
	 * switching on instruction types.
	 */
	public final int getID() {
		return id;
	}

	/**
	 * Returns an identification number associated with the instruction type.
	 * Each instruction class gets a unique ID which can be used to do efficient
	 * switching on instruction types.
	 * 
	 * @param c
	 *            the instruction class to get the ID for
	 */
	public static int getID(Class<? extends Instruction> c) {
		if (inst_id.containsKey(c)) {
			return inst_id.get(c);
		} else {
			int this_id = next_id++;
			inst_id.put(c, this_id);
			return this_id;
		}
	}

	/**
	 * Returns the instruction in <code>jasmin</code> format.
	 * 
	 * @return the instruction as a string
	 */
	public abstract String toAsm();

	/**
	 * Returns the number of stack slots that the stack height will change when
	 * this instruction is executed. A positive value means the stack will grow.
	 * A negative value means the stack will shrink.
	 * 
	 * @return the stack change
	 */
	public abstract int stackChange();

	/**
	 * Returns the index of the highest numbered local variable accessed by this
	 * instruction.
	 * 
	 * @return the highest local variable index accessed, or -1 if no local
	 *         variables are accessed.
	 */
	public int localAccess() {
		return -1;
	}

	/**
	 * Can this instruction transfer control to a label? If so, the
	 * {@link #getTarget} method returns the label.
	 * 
	 * @return whether or not the instruction can jump
	 */
	public boolean canJump() {
		return false;
	}

	/**
	 * Can this instruction transfer control to the succeeding instruction?
	 * 
	 * @return whether or not the instruction can fall through
	 */
	public boolean canFallThrough() {
		return true;
	}

	/**
	 * What is the target of any jump that this instruction performs? This
	 * function is only legal to call if {@link #canJump} returns
	 * <code>true</code>.
	 * 
	 * @return the target label
	 */
	public Label getTarget() {
		throw new RuntimeException("Instruction has no target");
	}

	/**
	 * Returns the specified numbered argument as given to the constructor.
	 * 
	 * @param i
	 *            the index of the argument.
	 * @return the specified argument.
	 */
	public Object getArg(int i) {
		return null;
	}
}
